/* Peter Banda, Daniel Cristofani, Dominic Tippabattuni                     */
/* 975530978    971012079          958842138                                */
/* VMM_Sim Implementation (assignment 5)                                    */
/* CS 538, Portland State University, Fall 2010                             */
/* Turned in November 18, 2010                                              */

/* statsprinter.c contains functions to print output somewhat gracefully.   */
/* It calls historycollector.c to get data.                                 */

#include "vmm.h"

//Utility macros for formatting trace info for the t command
#define TRACE_PER_LINE 8
#define FORM() mem_refs++; if((mem_refs % TRACE_PER_LINE) == 0) printf ("\n")
#define RW_COMMAND(x,add) printf("%c 0x%08lx ",convert(x),add);FORM()
#define LTPV_COMMAND(x) printf("%c ",convert(x));FORM()

//Timing info, since it's only this module that needs it.
//Also, putting it here allows it to be modified easily.
#define CYCLES_TO_STREAM_IN 20
#define CYCLES_TO_STREAM_OUT 20
#define HIT_CYCLES 1
#define MISS_CYCLES 1

//It might have been easier to just use char throughout instead of enum.
char convert(enum TypeOfAccess x)
{
    switch (x) {
    case r:
	return 'r';
    case l:
	return 'l';
    case t:
	return 't';
    case w:
	return 'w';
    case p:
	return 'p';
    case v:
	return 'v';
    default:
	return 0;
    }
}

//Detects when adding two ULONGS would overflow an ULONG, and exits with error
//message (takes the name of the overflowed variable as a third argument);
//if no overflow would occur, returns the result of the addition.
unsigned long safeAdd(unsigned long a, unsigned long b, char *name)
{
    if (ULONG_MAX - a < b)
	error
	    ("The variable %s has overflowed. Any output from this run is suspect.",
	     name);
    return a + b;
}

//Same, but for multiplication.
unsigned long safeMult(unsigned long a, unsigned long b, char *name)
{
    if (ULONG_MAX / a < b)
	error
	    ("The variable %s has overflowed. Any output from this run is suspect.",
	     name);
    return a * b;
}

//Computes the number of machine cycles taken when the VMM is used. Each virtual
//memory access requires three physical memory accesses (page directory, page table,
//user page) as well as 50,000 cycles per swap-in or swap-out.
unsigned long getVMMCycles()
{
    struct Stats stats = getStats();
    return safeAdd(
	    	safeMult(
			CYCLES_FOR_MASS_STORAGE_ACCESS,
        		safeAdd(stats.swapIns, stats.swapOuts, "swaps"),
		"VMMCycles"),
		safeMult(
			safeMult(3, CYCLES_FOR_MEMORY_ACCESS, "VMMCycles"),
			safeAdd(stats.reads, stats.writes, "accesses"),
		"VMMCycles"),
	"VMMCycles");
}

//Core output function, always runs unless the v command was used.
//Reconstitutes the statistics that are derived from others.
void printVMMStatistics(void)
{
    struct Stats stats;
    unsigned long accesses, noVMMCycles;
    stats = getStats();
    accesses = safeAdd(stats.reads, stats.writes, "accesses");
    noVMMCycles =
	safeAdd(safeMult
		(CYCLES_FOR_MEMORY_ACCESS, stats.reads, "noVMMCycles"),
		safeMult(CYCLES_FOR_MEMORY_ACCESS, stats.writes,
			 "noVMMCycles"), "noVMMCycles");
    printf("Total Accesses = %lu\n", accesses);
    printf("Total Reads = %lu\n", stats.reads);
    printf("Total Writes = %lu\n", stats.writes);
    printf("Cycles with VMM = %lu\n", getVMMCycles());
    printf("Cycles w/o VMM = %lu\n", noVMMCycles);
    printf("Swap-ins = %lu\n", stats.swapIns);
    printf("Swap-outs = %lu\n", stats.swapOuts);
    printf("Total Replacements = %lu\n", stats.replacements);
    printf("Total PD entries used = %lu\n", stats.existingPageTableCount);
    printf("PT total = %lu\n", stats.existingPageTableCount);
    printf("PT max = %lu\n", stats.residentPageTableMax);
    printf("User pages total = %lu\n", stats.existingUserPageCount);
    printf("User pages max = %lu\n", stats.residentUserPageMax);
    printf("Physical page frames = %d\n", noOfPageFrames);
}

void printVersionInfo(void)
{
    printf("VMM_SIM Version %s\n", VERSION_INFO);
    exit(0);
}

//Prints the trace information of the input, when the simulator executes
//with the t debug option
void printTraceInfo(void)
{
    int mem_refs = 0;
    struct CommandLogEntry *temp;
    printf("\nTrace Information:\n");
    temp = getCommandLog();
    while (temp != NULL) {
	switch (temp->access) {
	case r:
	case w:
	    RW_COMMAND(temp->access, temp->address);
	    break;
	case l:
	case t:
	case p:
	case v:
	    LTPV_COMMAND(temp->access);
	    break;
	default:
	    error("Somehow an 'enum TypeOfAccess' got the value %d.",
		  temp->access);
	}
	temp = temp->next;
    }
    printf("\n");
}
